/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.ssm.changeunit.util;

import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import com.ssm.changeunit.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * ChangeUnit을 Level 별로 순차적으로 탐색하는 클래스
 * 
 * @author 시리우스
 */
public class ChangeUnitTraveler {
    public enum Direct { Child, Parent };
    
    private Queue<String> queue;
    private List<String> travledList;    
    private Direct direct;
    private String startChangeUnitId;
    private String endChangeUnitId;
    
    /**
     * 생성자
     */
    public ChangeUnitTraveler() {
        this(Direct.Parent);
    }
    
    /**
     * 생성자
     */
    public ChangeUnitTraveler(Direct direct) {
        this(direct, null, null);
    }
    
    /**
     * 생성자
     * 
     * @param direct
     * @param startChangeUnitId
     * @param endChangeUnitId
     */
    public ChangeUnitTraveler(Direct direct, String startChangeUnitId, String endChangeUnitId) {
        queue = new LinkedList<String>();
        travledList = new ArrayList<String>();
        this.direct = direct;
        this.startChangeUnitId = startChangeUnitId;
        this.endChangeUnitId = endChangeUnitId;
    }
    
    /**
     * 초기화
     */
    public void init() throws ChangeUnitException {
        queue.clear();
        travledList.clear();
        travledList.add("0");
        
        if(startChangeUnitId == null) {
            Branch branch = null;
            
            try {
                branch = new BranchFactory().createProduct(Environment.getCurrentBranch());
            }
            catch (IOException ex) {
                throw new ChangeUnitException(ex);
            }

            // 대기 큐에 최상위 ChangeUnit 삽입
            pushToWaitQueue(branch.getCurrentChangeUnit().getChangeUnitId());
        }
        else {
            // 대기 큐에 최상위 ChangeUnit 삽입
            pushToWaitQueue(startChangeUnitId);
        }
    }
    
    /**
     * 다음 ChangeUnit이 있는지 검사한다.
     * @return
     */
    public boolean hasNext() {
        return !queue.isEmpty();
    }
    
    /**
     * 다음 ChangeUnit을 가져옵니다.
     * 
     * @return
     */
    public ChangeUnit getNextChangeUnit() throws ChangeUnitException {
        // 다음 ChangeUnit을 가져옵니다.
        String nextChangeUnitId = queue.poll();
        
        travledList.add(nextChangeUnitId);

        ChangeUnit nextChangeUnit = new ChangeUnitFactory().createProduct(nextChangeUnitId);
        
        if(endChangeUnitId != null && endChangeUnitId.equals(nextChangeUnitId)) {
            queue.clear();
        }
        else {
            if(direct == Direct.Parent) {
                // 부모를 Queue에 삽입
                List<ChangeUnit> parents = nextChangeUnit.getParents();

                for(ChangeUnit parent : parents) 
                    pushToWaitQueue(parent.getChangeUnitId());
            }
            else if(direct == Direct.Child) {
                // 자식을 Queue에 삽입
                List<ChangeUnit> childs = nextChangeUnit.getChilds();

                for(ChangeUnit child : childs) 
                    pushToWaitQueue(child.getChangeUnitId());
            }
        }
        
        return nextChangeUnit;
    }
    
    /**
     * 대기 큐에 삽입합니다.
     * @param changeUnitId
     */
    private void pushToWaitQueue(String changeUnitId) {
        if(!travledList.contains(changeUnitId) && !queue.contains(changeUnitId))
            queue.add(changeUnitId);
    }
}
